/**
 * @file queue.c
 * @brief 实现一个线程安全的队列
 *
 * 该文件实现了一个基于链表的线程安全队列，包括队列的创建、销毁、数据入队、数据出队等操作。
 * 使用互斥锁（mutex）和条件变量（condition variable）来保证线程安全。
 *
 * @note 该队列的实现假设队列中的元素都是动态分配的，因此在出队时不会释放元素的内存。
 * 用户需要在使用完毕后自行释放。
 */
#include <stdlib.h>
#include <pthread.h>
#include <stdatomic.h>
#include <sys/sysinfo.h>

#include "queue.h"

struct tag_queue {
    size_t max_size;  // 队列最大容量
    size_t data_size;  // data_list中元素个数
    unsigned int idx;
    work_task_t *data_list;
    work_task_t *data_list_tail;
    pthread_cond_t not_full;
    pthread_cond_t not_empty;
    pthread_mutex_t mutex_lock;
};

queue_t* queue_create(size_t max_size)
{
    queue_t *queue = (queue_t *)malloc(sizeof(queue_t));
    if (queue == NULL) {
        return NULL;
    }
    queue->idx = 0;
    queue->data_size = 0;
    queue->max_size = max_size;
    queue->data_list = NULL;
    queue->data_list_tail = NULL;
    pthread_cond_init(&queue->not_empty, NULL);
    pthread_cond_init(&queue->not_full, NULL);
    pthread_mutex_init(&queue->mutex_lock, NULL);
    return queue;
}

static void queue_free_data(work_task_t *list)
{
    work_task_t *tmp = NULL;
    work_task_t *current = NULL;

    current = list;
    while (current != NULL) {
        tmp = current;
        current = current->next;
        free(tmp);
    }
    return;
}

int queue_destory(queue_t *queue)
{
    printf("queue_destory %u\n", queue->data_size);
    queue_free_data(queue->data_list);
    queue->data_list = NULL;
    queue->data_list_tail = NULL;
    pthread_cond_destroy(&queue->not_full);
    pthread_cond_destroy(&queue->not_empty);
    pthread_mutex_destroy(&queue->mutex_lock);
    free(queue);
    return 0;
}

work_task_t *queue_alloc_data(queue_t *queue)
{
    struct sysinfo si;
    work_task_t *data = NULL;

    data = (work_task_t *)malloc(sizeof(work_task_t));
    if (data == NULL) {
        return NULL;
    }
    data->next = NULL;

    if (sysinfo(&si) != 0) {
        data->task_id = -1;
    } else {
        data->task_id = si.uptime;
    }
    return data;
}

void queue_free_task(queue_t *queue, work_task_t *data)
{
    if (data == NULL) {
        return;
    }
    free(data);
    return;
}

int queue_put(queue_t *queue, work_task_t *data)
{
    if (data == NULL) {
        return -1;
    }
    pthread_mutex_lock(&queue->mutex_lock);
    while (queue->data_size == queue->max_size) {
        printf("queue_put\n");
        pthread_cond_wait(&queue->not_full, &queue->mutex_lock);
    }
    data->next = NULL;
    if (queue->data_list_tail == NULL || queue->data_list == NULL) {
        queue->data_list = data;
        queue->data_list_tail = data;
    } else {
        queue->data_list_tail->next = data;
    }
    queue->data_size++;
    pthread_mutex_unlock(&queue->mutex_lock);
    pthread_cond_signal(&queue->not_empty);
    return 0;
}

work_task_t* queue_take(queue_t *queue)
{
    work_task_t *node_to_remove = NULL;
    pthread_mutex_lock(&queue->mutex_lock);
    node_to_remove = queue->data_list;
    while (queue->data_size == 0 || node_to_remove == NULL) {
        pthread_cond_wait(&queue->not_empty, &queue->mutex_lock);
        node_to_remove = queue->data_list;
    }
    queue->data_list = node_to_remove->next;
    if (queue->data_list == NULL) {
        queue->data_list_tail = NULL;
    }
    queue->data_size--;
    pthread_mutex_unlock(&queue->mutex_lock);
    pthread_cond_signal(&queue->not_full);
    return node_to_remove;
}

void queue_notify_all(queue_t *queue)
{
    pthread_cond_broadcast(&queue->not_empty);
    pthread_cond_broadcast(&queue->not_full);
    return;
}
